package controllers

import (
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego/httplib"
	"github.com/astaxie/beego/logs"
	"github.com/eclipse/paho.mqtt.golang"
	"smart_home/mqtt/models"
	"smart_home/mqtt/system"
	"strconv"
	"strings"
	"sync"
	"time"
	"tools"
	"xfyun"
)

type SayController struct {
	system.Controller
}

var soundCache = sync.Map{}
var scene = "main"

func (c *SayController) Exece(client mqtt.Client, message mqtt.Message, clientId string) {
	//if string(message.Payload()) == "begin" {
	//	logs.Debug("begin")
	//}
	//
	//if string(message.Payload()) == "end" {
	//	logs.Debug("end")
	//}
	//
	//cor := client.OptionsReader()
	//logs.Debug("options:", cor.ClientID())
	//
	//c.begin(client, message, clientId)
	//c.enqueue(client, message, clientId)
	//c.end(client, message, clientId)

	c.send(client, message, clientId)
}

func (c *SayController) send(client mqtt.Client, message mqtt.Message, clientId string) {
	status := "reading"

	if string(message.Payload()) == "begin" {
		status = "begin"
	}
	if string(message.Payload()) == "end" {
		status = "end"
	}

	order := strings.Split(message.Topic(), "/")[3]
	logs.Debug("order:", order)

	str, err := httplib.Post("http://api.rcenet.com/v2/xfyun/voice/parser").
		Header("sta", clientId).
		Header("order", order).
		Header("status", status).
		Body(message.Payload()).String()

	// 下发数据、控制
	if status == "end" {

	}

	logs.Debug(str, err)
}

// 语音入队
func (c *SayController) enqueue(client mqtt.Client, message mqtt.Message, clientId string) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("err recover:", err)
		}
	}()

	// 语音信息序号
	order, _ := strconv.Atoi(strings.Split(message.Topic(), "/")[3])

	logs.Debug("topic:", message.Topic(), " / client_id:", clientId, " / order:", order, " / len:", len(message.Payload()))

	payload := string(message.Payload())
	if payload == "begin" || payload == "end" {
		return
	}

	soundCache.Store(fmt.Sprintf("%s%d", clientId, order), message.Payload())
}

// 开始
func (c *SayController) begin(client mqtt.Client, message mqtt.Message, clientId string) {
	if string(message.Payload()) != "begin" {
		return
	}
}

/*
code	string	结果码(具体见错误码)
data	array	结果数据
desc	string	描述
sid	string	会话ID
*/
type custom struct {
	Code string       `json:"code"`
	Data []customData `json:"data"`
	Desc string       `json:"desc"`
	Sid  string       `json:"sid"`
}

/*
sub	string	业务类型：iat（识别），nlp（语义），tpp（后处理），itrans（翻译）
text	object/string	识别结果：详细结果（object），精简结果（string）
intent	object	语义结果
content	object/string	后处理 (string)，翻译 (object)等结果
result_id	number	结果序号，同一业务逐渐递增
*/
type customData struct {
	Sub      string                 `json:"sub"`
	Text     interface{}            `json:"text"`
	Intent   map[string]interface{} `json:"intent"`
	Content  interface{}            `json:"content"`
	ResultId int64                  `json:"result_id"`
}

// xfyun_after response
type response struct {
	Answer response_answer `json:"answer"`
	Data   interface{}     `json:"data"`
}

type response_answer struct {
	Text string `json:"text"`
	Type string `json:"type"`
}

func (c *custom) getIntent() map[string]interface{} {
	for _, v := range c.Data {
		if v.Sub == "nlp" && len(v.Intent) > 0 {
			return v.Intent
		}
	}
	return nil
}

// 结束
func (c *SayController) end(client mqtt.Client, message mqtt.Message, clientId string) {
	if string(message.Payload()) != "end" {
		return
	}

	// 等待100毫秒 防止end提前收到，而数据还没接收完成
	<-time.After(100 * time.Millisecond)

	user, err := models.GetUserBySta(clientId)
	if err != nil {
		logs.Error(err)
		return
	}

	equipment, err := models.GetEquipmentBySta(clientId, user.Id)
	if err != nil {
		logs.Error(err)
		return
	}

	logs.Warn("client_id:%s, equipment: %+v, user: %+v", clientId, equipment, user)

	// 语音信息序号
	order, _ := strconv.Atoi(strings.Split(message.Topic(), "/")[3])

	// 对语音slice排序组合成string
	body := []byte("")
	for i := 1; i < order; i++ {
		// 获取语音文件
		key := fmt.Sprintf("%s%d", clientId, i)
		data, ok := soundCache.Load(key)

		// 判断语音文件是否存在，存在则合并文件，不存在则跳出识别
		if !ok {
			logs.Error("client_id %s say error: 语音文件%d为空", clientId, i)
			break
		}

		body = tools.BytesCombine(body, data.([]byte))

		// 删除语音文件
		soundCache.Delete(key)
	}

	// 写入文件
	//fname := fmt.Sprintf("./logs/%d.pcm", rand.Int())
	//if ioutil.WriteFile(fname, body, 0644) == nil {
	//	logs.Debug("fileName:", fname)
	//}

	userParams := map[string]interface{}{
		"user_id":     user.Id,
		"sta":         equipment.Sta,
		"device_type": equipment.Type,
	}

	//logs.Debug("equipment:%+v / params:%+v / body len:%d", equipment, userParams, len(body))

	res, err := xfyun.NewAiui().
		SetAuthId(clientId).
		SetUserParams(userParams).
		SetScene(scene).
		Audio(body)

	if err != nil {
		logs.Error(err)
		return
	}

	logs.Debug("intent:", res)

	custom := &custom{}
	if err := json.Unmarshal([]byte(res), custom); err != nil {
		logs.Error(err)
		return
	}

	if custom.Code != "0" {
		logs.Error("xfyun error:", res)
		return
	}

	hr, err := httplib.Post("http://api.rcenet.com/v2/xfyun/after/custom").JSONBody(custom.getIntent())
	resp := &response{}
	if err := hr.ToJSON(resp); err != nil {
		logs.Error("xfyun after error:", err)
		return
	}

	logs.Debug("resp: %+v", resp)

	// 如果设备的类型不是无外放设备 则下发数据
	if equipment.Type != 1 {

	}
}
